#pragma once
#include <iostream>
#include <memory>
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include "Err.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <functional>
#include <unordered_map>
#include "ringQueue.hpp"
#include "Thread.hpp"
using namespace std;

const static uint16_t default_port = 8080;
using func_t = function<string(string &)>;
class UdpServer
{
public:
    UdpServer(func_t fuc, uint16_t port = default_port)
        : fuc_(fuc), port_(port)
    {
    }
    UdpServer(uint16_t port = default_port)
        : port_(port)
    {
        Producer_ = new Thread(1, bind(&UdpServer::Recv, this));
        Customer_ = new Thread(1, bind(&UdpServer::Broadcast, this));
    }
    ~UdpServer()
    {
        Customer_->join();
        Producer_->join();
    }
    void Start()
    {
        sock_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock_ < 0)
        {
            cerr << "创建套接字失败:" << strerror(errno) << endl;
            exit(SOCK_ERR);
        }
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = bind(sock_, (sockaddr *)(&local), sizeof(local));
        if (n < 0)
        {
            cerr << "绑定失败:" << strerror(errno) << endl;
            exit(BIND_ERR);
        }
        cout << "初始化成功！\n";
        Producer_->run();
        Customer_->run();

    }
    void Adduser(string name, struct sockaddr_in peer)
    {
        OnlineUsers_[name] = peer;
    }
    void Broadcast()
    {
        while (1)
        {
            string tmp;
            rq_.pop(tmp);
            for (auto user : OnlineUsers_)
            {
                ssize_t m = sendto(sock_, tmp.c_str(), tmp.size(), 0, (sockaddr *)&(user.second), sizeof(user.second));
                if (m < 0)
                {
                    break;
                }
                //cout << "服务器测试：" << user.first << tmp.c_str() << endl;
            }
        }
    }
    void Recv()
    {
        struct sockaddr_in peer;
        socklen_t size = sizeof(peer);
        while (1)
        {
            char buf[2048];
            ssize_t n = recvfrom(sock_, buf, sizeof(buf) - 1, 0, (sockaddr *)(&peer), &size);
            if (n < 0)
            {
                continue;
            }
            buf[n] = '\0';
            auto peerIp = peer.sin_addr.s_addr;
            auto peerPort = peer.sin_port;
            string name = to_string(ntohl(peerIp));
            name += '-';
            name += to_string(ntohs(peerPort));
            name += ": ";

            Adduser(name, peer);
            buf_.clear();
            buf_ += name;
            buf_ += buf;
            rq_.push(buf_);
        }
    }

private:
    uint16_t port_;
    int sock_;
    func_t fuc_;
    unordered_map<string, struct sockaddr_in> OnlineUsers_;
    string buf_;
    ringQueue<string> rq_;
    Thread *Producer_;
    Thread *Customer_;
};